﻿using Gentings.Data;
using Gentings.Data.Internal;

namespace Gentings.Extensions.Tagables
{
    /// <summary>
    /// 标签分隔符。
    /// </summary>
    public static class TagSeparator
    {
        /// <summary>
        /// 标签分隔符。
        /// </summary>
        public const char Current = ',';
        private const string Separators = ",、，|";

        /// <summary>
        /// 获取标签数组列表。
        /// </summary>
        /// <param name="tags">当前标签字符串集合，以<see cref="Current"/>分割。</param>
        /// <returns>返回标签数组列表。</returns>
        public static string[]? ToTagArray(this string? tags)
        {
            return tags?.Split(Current, StringSplitOptions.RemoveEmptyEntries);
        }

        /// <summary>
        /// 分割并且返回唯一的标签集合，以<see cref="Current"/>分割。
        /// </summary>
        /// <param name="tags">当前标签字符串。</param>
        /// <returns>返回处理后的标签集合。</returns>
        public static string? ToDistinctTagString(this string? tags)
        {
            if (string.IsNullOrEmpty(tags)) return null;
            tags = tags.Replace(" ", string.Empty);
            tags = string.Join(Current, tags.Split(Separators.ToCharArray(), StringSplitOptions.RemoveEmptyEntries).Distinct(StringComparer.OrdinalIgnoreCase));
            return tags;
        }

        /// <summary>
        /// 添加<paramref name="target"/>的标签索引。
        /// </summary>
        /// <param name="context">事物上下文实例。</param>
        /// <param name="target">当前实例对象。</param>
        /// <param name="separator">分隔符。</param>
        /// <returns>返回添加结果。</returns>
        public static bool CreateTagIndex<TTarget, TTag, TTagIndex>(this IDbTransactionContext<TTarget> context, TTarget target, char separator = Current)
            where TTarget : ITagable, new()
            where TTag : TagBase, new()
            where TTagIndex : TagIndexBase, new()
        {
            if (string.IsNullOrWhiteSpace(target.Tags))
                return true;

            var indexdb = context.As<TTagIndex>();
            return indexdb.CreateIndex<TTarget, TTag, TTagIndex>(target, separator);
        }

        /// <summary>
        /// 添加<paramref name="target"/>的标签索引。
        /// </summary>
        /// <param name="context">事物上下文实例。</param>
        /// <param name="target">当前实例对象。</param>
        /// <param name="separator">分隔符。</param>
        /// <returns>返回添加结果。</returns>
        public static async Task<bool> CreateTagIndexAsync<TTarget, TTag, TTagIndex>(this IDbTransactionContext<TTarget> context, TTarget target, char separator = Current)
            where TTarget : ITagable, new()
            where TTag : TagBase, new()
            where TTagIndex : TagIndexBase, new()
        {
            if (string.IsNullOrWhiteSpace(target.Tags))
                return true;

            var indexdb = context.As<TTagIndex>();
            return await indexdb.CreateIndexAsync<TTarget, TTag, TTagIndex>(target, separator);
        }

        /// <summary>
        /// 更新<paramref name="target"/>的标签索引。
        /// </summary>
        /// <param name="context">事物上下文实例。</param>
        /// <param name="target">当前实例对象。</param>
        /// <param name="separator">分隔符。</param>
        /// <returns>返回添加结果。</returns>
        public static bool UpdateTagIndex<TTarget, TTag, TTagIndex>(this IDbTransactionContext<TTarget> context, TTarget target, char separator = Current)
            where TTarget : ITagable, new()
            where TTag : TagBase, new()
            where TTagIndex : TagIndexBase, new()
        {
            var indexdb = context.As<TTagIndex>();
            indexdb.Delete(x => x.IndexedId == target.Id);
            if (string.IsNullOrWhiteSpace(target.Tags))
                return true;

            return indexdb.CreateIndex<TTarget, TTag, TTagIndex>(target, separator);
        }

        /// <summary>
        /// 更新<paramref name="target"/>的标签索引。
        /// </summary>
        /// <param name="context">事物上下文实例。</param>
        /// <param name="target">当前实例对象。</param>
        /// <param name="separator">分隔符。</param>
        /// <returns>返回添加结果。</returns>
        public static async Task<bool> UpdateTagIndexAsync<TTarget, TTag, TTagIndex>(this IDbTransactionContext<TTarget> context, TTarget target, char separator = Current)
            where TTarget : ITagable, new()
            where TTag : TagBase, new()
            where TTagIndex : TagIndexBase, new()
        {
            var indexdb = context.As<TTagIndex>();
            await indexdb.DeleteAsync(x => x.IndexedId == target.Id);
            if (string.IsNullOrWhiteSpace(target.Tags))
                return true;

            return await indexdb.CreateIndexAsync<TTarget, TTag, TTagIndex>(target, separator);
        }

        private static bool CreateIndex<TTarget, TTag, TTagIndex>(this IDbTransactionContext<TTagIndex> context, TTarget target, char separator = Current)
            where TTarget : ITagable, new()
            where TTag : TagBase, new()
            where TTagIndex : TagIndexBase, new()
        {
            var tags = target.Tags!.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries)
                .Select(x => x.Trim())
                .Distinct(StringComparer.OrdinalIgnoreCase)
                .ToArray();
            var tagdb = context.As<TTag>();
            foreach (var tag in tags)
            {
                var current = tagdb.Find(x => x.Name == tag);
                if (current == null)
                {
                    current = new TTag { Name = tag };
                    if (!tagdb.Create(current))
                        return false;
                }
                var index = new TTagIndex { IndexedId = target.Id, TagId = current.Id };
                if (!context.Create(index))
                    return false;
            }
            return true;
        }

        private static async Task<bool> CreateIndexAsync<TTarget, TTag, TTagIndex>(this IDbTransactionContext<TTagIndex> context, TTarget target, char separator = Current)
            where TTarget : ITagable, new()
            where TTag : TagBase, new()
            where TTagIndex : TagIndexBase, new()
        {
            var tags = target.Tags!.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries)
                .Select(x => x.Trim())
                .Distinct(StringComparer.OrdinalIgnoreCase)
                .ToArray();
            var tagdb = context.As<TTag>();
            foreach (var tag in tags)
            {
                var current = await tagdb.FindAsync(x => x.Name == tag);
                if (current == null)
                {
                    current = new TTag { Name = tag };
                    if (!await tagdb.CreateAsync(current))
                        return false;
                }
                var index = new TTagIndex { IndexedId = target.Id, TagId = current.Id };
                if (!await context.CreateAsync(index))
                    return false;
            }
            return true;
        }

        /// <summary>
        /// 初始化查询上下文。
        /// </summary>
        /// <param name="context">查询上下文。</param>
        /// <param name="tags">标签Id列表。</param>
        public static void Where<TTarget, TTag, TTagIndex>(this IQueryContext<TTarget> context, int[] tags)
            where TTarget : ITagable, new()
            where TTag : TagBase, new()
            where TTagIndex : TagIndexBase, new()
        {
            context.InnerJoin<TTagIndex>((t, i) => t.Id == i.IndexedId)
                .InnerJoin<TTagIndex, TTag>((i, t) => i.TagId == t.Id)
                .Where<TTag>(t => t.Id.Included(tags));
        }
    }
}
